package com.strong.sample.utils;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.strong.utils.JSON;
import com.strong.utils.StrongUtils;
import com.strong.utils.exception.StrongRuntimeException;
import com.strong.utils.mvc.pojo.view.ReplyVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;

import static com.strong.utils.jpa.JpaConstants.STR_INCLUDE_PROPERTIES_NAME;
import static org.springframework.test.util.AssertionErrors.*;

/**
 * 模拟匹配器
 *
 * @author simen
 * @date 2024/10/28
 */
@Slf4j
public class MockMatcher {

    /**
     * 判断返回内容是否为ReplyVO对象，且返回字段包含在strsIncludeField范围内
     *
     * @param intCode          断言返回的代码
     * @param strAssertMessage 断言消息
     * @return {@link ResultMatcher }
     */
    public static ResultMatcher isExceptionReplyString(final Integer intCode, final String strAssertMessage) {
        return result -> {
            logResult(result);
            JsonNode jsonNodeData = getReplyJsonNode(intCode, result);
            Assert.isTrue(jsonNodeData instanceof TextNode, "返回的不是异常消息");
            String strMessage = jsonNodeData.asText();
            Assert.notBlank(strMessage, "返回的异常消息为空");
            Assert.isTrue(StrUtil.contains(strMessage, strAssertMessage),
                    "返回的异常消息错误\n断言：{}\n返回：{}\n", strAssertMessage, strMessage);
        };
    }

    /**
     * 判断返回内容是否为ReplyVO对象，且返回字段包含在strsIncludeField范围内
     *
     * @param intCode 断言返回的代码
     * @return {@link ResultMatcher }
     */
    public static ResultMatcher isExceptionReplyJson(final Integer intCode, final Map<String, String> mapAssertFiledMessage) {
        return result -> {
            logResult(result);
            Assert.notEmpty(mapAssertFiledMessage, "断言的字段消息集合为空");
            JsonNode jsonNodeData = getReplyJsonNode(intCode, result);
            Assert.notNull(jsonNodeData, "返回的异常消息为空");

            for (String strFiledName : mapAssertFiledMessage.keySet()) {
                String strAssertMessage = mapAssertFiledMessage.get(strFiledName);

                JsonNode jsonNodeFiledMessage = jsonNodeData.get(strFiledName);
                Assert.notNull(jsonNodeFiledMessage, "【{}】字段返回消息为空", strFiledName);

                String strFiledMessage = jsonNodeFiledMessage.asText();
                Assert.isTrue(StrUtil.equals(strFiledMessage, strAssertMessage),
                        "返回的异常消息错误\n断言：{}\n返回：{}\n", strAssertMessage, strFiledMessage);
            }
        };
    }

    /**
     * 判断返回内容是否为ReplyVO对象，且返回字段包含在strsIncludeField范围内
     *
     * @param intCode          断言返回的代码
     * @param strsIncludeField 包含字段
     * @return {@link ResultMatcher }
     */
    public static ResultMatcher isReplyModel(final Integer intCode, final String... strsIncludeField) {
        return result -> {
            JsonNode jsonNodeData = getReplyJsonNode(intCode, result);
            assertModelFields(jsonNodeData, strsIncludeField);
        };
    }

    /**
     * 判断返回内容是否为ReplyVabListVO对象，且返回字段包含在strsIncludeField范围内
     *
     * @param intCode          int代码
     * @param strsIncludeField STRS包含字段
     * @return {@link ResultMatcher }
     */
    public static ResultMatcher isReplyList(final Integer intCode, final String... strsIncludeField) {
        return result -> {
            JsonNode jsonNodeData = getReplyJsonNode(intCode, result);

            // 如果data的返回字段中有list，则返回的为队列
            // 此时使用第一条记录作为对比字段的模型
            JsonNode jsonNodeList = jsonNodeData.get("list");
            JsonNode jsonNodeTotal = jsonNodeData.get("total");
            assertNotNull("返回的不是ReplyVabListVO<?>对象", jsonNodeTotal);
            if (IterUtil.isNotEmpty(jsonNodeList)) {
                JsonNode jsonNodeModel = jsonNodeList.get(0);
                assertModelFields(jsonNodeModel, strsIncludeField);
            }
        };
    }

    /**
     * 判断返回内容是否为ReplyVO对象，且data对象为String，如果strData非空，则断言匹配strData
     *
     * @param strData str数据
     * @return {@link ResultMatcher }
     */
    public static ResultMatcher isReplyString(String strData) {
        return result -> {
            ReplyVO<String> replyVO = JSON.parseReply(result.getResponse().getContentAsString(), String.class);
            System.out.println(replyVO + "===============================");
            if (StrUtil.isNotBlank(strData)) {
                Assert.equals(replyVO.getData(), strData);
            }
        };
    }

    /**
     * 通过反射 获取classz类中，可以序列化含字段名称的数组
     *
     * @param classz classz
     * @return {@link String[] }
     */
    public static String[] getIncludeFiledNames(Class<?> classz) {
        try {
            // 获取可序列化的字段名数组
            Field field = ReflectUtil.getField(classz, STR_INCLUDE_PROPERTIES_NAME);
            if (ObjUtil.isNotNull(field)) {
                return (String[]) field.get(null);
            } else {
                return new String[]{};
            }
        } catch (IllegalAccessException e) {
            throw new StrongRuntimeException(String.format("无法获取[%s]可序列化字段名常量", classz.getName()));
        }
    }

    /**
     * 断言模型字段在指定的字段名数组中
     *
     * @param jsonNodeData     Json节点数据
     * @param strsIncludeField 包含字段
     */
    private static void assertModelFields(JsonNode jsonNodeData, String[] strsIncludeField) {
        // 断言返回字段模型的字段，都包含在strsIncludeField中
        if (ArrayUtil.isNotEmpty(strsIncludeField)) {
            Iterator<String> iterFiledName = jsonNodeData.fieldNames();
            while (iterFiledName.hasNext()) {
                String strFiledName = iterFiledName.next();
                Assert.isTrue(StrUtil.containsAny(strFiledName, strsIncludeField),
                        "返回对象的[{}]字段不包含在数组{}中", strFiledName, ArrayUtil.toString(strsIncludeField));
                // log.info("返回对象的[{}]字段包含在数组{}中", strFiledName, ArrayUtil.toString(strsIncludeField));
            }
        }
    }

    /**
     * 获取回复json节点
     *
     * @param intCode int代码
     * @param result  结果
     * @return {@link JsonNode }
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    private static JsonNode getReplyJsonNode(Integer intCode, MvcResult result) throws UnsupportedEncodingException {
        // 获取服务器返回的文本
        String strContent = result.getResponse().getContentAsString();
        assertTrue("服务器返回内容为空", StrUtil.isNotBlank(strContent));

        JsonNode jsonNode = JSON.parseJsonNode(strContent);
        assertNotNull("返回ReplyVO对象为空", jsonNode);

        JsonNode jsonNodeData = jsonNode.get("data");
        JsonNode jsonNodeMsg = jsonNode.get("msg");
        JsonNode jsonNodeCode = jsonNode.get("code");
        assertTrue("返回的参数存在空值", !ObjUtil.hasNull(jsonNodeCode, jsonNodeMsg, jsonNodeData));

        assertEquals("返回的$.code不匹配", intCode, jsonNode.get("code").asInt());
        assertTrue("返回的$.msg为空", StrUtil.isNotBlank(jsonNode.get("msg").asText()));
        return jsonNodeData;
    }

    /**
     * 记录结果
     *
     * @param result 结果
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    private static void logResult(MvcResult result) throws UnsupportedEncodingException {
        log.info("服务器返回：{}", result.getResponse().getContentAsString());
    }
}
